#define WIN32_LEAN_AND_MEAN
#include <assert.h>
#include "Semaphore.h"

// Private globals
#ifndef WIN32
    static pthread_mutexattr_t  *g_sMutexAttr=NULL;
    static void FunMutexAttrInit();
    static pthread_once_t g_sMutexAttrInit = PTHREAD_ONCE_INIT;
#endif

CMutex::CMutex()
{
#ifdef WIN32
    ::InitializeCriticalSection(&fMutex);
    fHolder = 0;
    fHolderCount = 0;
#else 
    (void)pthread_once(&g_sMutexAttrInit, FunMutexAttrInit);
    (void)pthread_mutex_init(&fMutex, g_sMutexAttr);
    
    fHolder = 0;
    fHolderCount = 0;
#endif
}


#ifndef WIN32
void FunMutexAttrInit()
{
    g_sMutexAttr = (pthread_mutexattr_t*)malloc(sizeof(pthread_mutexattr_t));
    ::memset(g_sMutexAttr, 0, sizeof(pthread_mutexattr_t));
    pthread_mutexattr_init(g_sMutexAttr);
}
#endif

CMutex::~CMutex()
{
#ifdef WIN32
    ::DeleteCriticalSection(&fMutex);
#else
    pthread_mutex_destroy(&fMutex);
#endif
}

void        CMutex::RecursiveLock()
{
    // We already have this mutex. Just refcount and return
    if (pthread_self() == fHolder)
    {
        fHolderCount++;
        return;
    }
#ifdef WIN32
    ::EnterCriticalSection(&fMutex);
#else
    (void)pthread_mutex_lock(&fMutex);
#endif
    assert(fHolder == 0);
    fHolder = pthread_self();
    fHolderCount++;
    assert(fHolderCount == 1);
}

void        CMutex::RecursiveUnlock()
{
    if (pthread_self() != fHolder)
        return;
        
    assert(fHolderCount > 0);
    fHolderCount--;
    if (fHolderCount == 0)
    {
        fHolder = 0;
#ifdef WIN32
        ::LeaveCriticalSection(&fMutex);
#else
        pthread_mutex_unlock(&fMutex);
#endif
    }
}

bool      CMutex::RecursiveTryLock()
{
    // We already have this mutex. Just refcount and return
    if (pthread_self() == fHolder)
    {
        fHolderCount++;
        return true;
    }

#ifdef WIN32
    bool theErr = (bool)::TryEnterCriticalSection(&fMutex); // Return values of this function match our API
    if (!theErr)
        return theErr;
#else
    int theErr = pthread_mutex_trylock(&fMutex);
    if (theErr != 0)
    {
        assert(theErr == EBUSY);
        return false;
    }
#endif
    assert(fHolder == 0);
    fHolder = pthread_self();
    fHolderCount++;
    assert(fHolderCount == 1);
    return true;
}
